<html xmlns="http://www.w3.org/1999/xhtml">

	<head>
	
<style type="text/css">
body {
	padding: 0;
	margin: 0;
}
.default-hexa {
	fill: #f0f0f0;
	stroke-width: 1px;
	stroke: #000;
}
.connecting-hexa {
	fill: #f1f1f1;
	stroke-width: 1px;
	stroke: #999;
}
.auxiliary-hexa {
	fill: #fff;
	stroke-width: 1px;
	stroke: #999;
}
.invisible-hexa {
    opacity: 0;
	fill: #fff;
	stroke-width: 1px;
	stroke: #999;
}
</style>

<script type="text/javascript" src="jquery.js"></script>

<script type="text/javascript">

function object(o) {
	function F() {}
	F.prototype = o;
	return new F();
}

function S(arg) {
	
	/* Arguments may vary. Path may be one case, another may be the svg tag, et cetera. */
	if (arg.tagName == "path") {
	
		/* This core function is for something really important: matching the selected G with its own Hexa Object */
		for (var i = 0; i < S.hexaGramas.length; i ++) {
		
			for (var j = 0; j < S.hexaGramas[i].hexas.length; j ++) {
				
				if (arg == S.hexaGramas[i].hexas[j].path) {
					return S.hexaGramas[i].hexas[j];
				}
				
			}
			
			/* If it wasn't found in the main hexas directory, it might be an auxiliary hexa */
			for (var j = 0; j < S.hexaGramas[i].auxHexas.length; j ++) {
				
				if (arg == S.hexaGramas[i].auxHexas[j].path) {
					return S.hexaGramas[i].auxHexas[j];
				}
				
			}
		
		}	
		
		
		return false;
	} else if (arg.tagName == "svg") {
		
		for (var i = 0; i < S.hexaGramas.length; i ++) {
			
			if (arg == S.hexaGramas[i].svg) {
				return S.hexaGramas[i];
			}
			
		}
		
		return false;
	} else if (arg.tagName == "div") {
	
		// Uf, for God's sake, checking if this div has something, right?
		for (var i = 0; i < arg.childNodes.length; i ++) {
			
			if (arg.childNodes[i].tagName == "svg") {
			
				for (var j = 0; j < S.hexaGramas.length; j ++) {
				
					if (arg.childNodes[i] == S.hexaGramas[j].svg) {
						return S.hexaGramas[j];
					}
				
				}
			
			}
			
		}
		return false;
		
	}
	
}

S.svg = function (tagName) {
	return document.createElementNS("http://www.w3.org/2000/svg", tagName);
}
S.el = function(el) {
	return document.getElementById(el);
}

S.point = function(x, y) {

	this.x = x;
	this.y = y;
	
}

S.connection = function(origin, target, relationship) {
	
	this.origin = origin; 
	this.target = target;
	this.relationship = relationship;
	
}

S.hexaGramas = new Array(); 

function HexaGrama(width, height, ro) {
	
	this.svg = S.svg("svg");
	this.svg.setAttribute("width", width + "px");
	this.svg.setAttribute("height", height + "px");

	this.ro = ro;
	
	this.hexas = new Array();
	this.connections = new Array();
	
	this.auxLayer = S.svg("g");
	
	this.auxHexas = new Array();
	this.svg.appendChild(this.auxLayer);
	
	S.hexaGramas.push(this); 
	
	this.svg.ondblclick = function (e) {
		
		if (e.target.tagName == "svg") {
			
			var hexaGrama = S(e.target);
			
			hexaGrama.addHexa(e.clientX, e.clientY);
			
		}
		
	}
	
	this.svg.onmouseup = function(e) {
		
		if (e.target.tagName == "svg") {
			// If the mouse goes up in the background, it means there's nothing to do
			currentSVG = S(e.target);
		} else if (e.target.tagName == "div") {
			
			// If the mouse goes up in a div, it might be the right div, for Safari, or even for Firefox!!!
			currentSVG = S(e.target);
			
		} else {
			// If the mouse goes up on a path, we must make a few checks
			currentSVG = S(e.target.parentNode.parentNode);
			
			// Check if it goes up on an auxiliary hexa. If yes, we must build it :D
			if (e.target.getAttribute("class") == "auxiliary-hexa") {
				
				// First we must get the AuxHexa Obj
				var auxHexa = S(e.target);
				
				currentSVG.addHexa(auxHexa.x, auxHexa.y);
				
			}
		}
		
		if (currentSVG) {
		
			// Whatever happens, we end up deleting all auxiliary hexas.
			while (currentSVG.auxLayer.childNodes.length > 0) {
				currentSVG.auxLayer.removeChild(currentSVG.auxLayer.firstChild);
			}
			
		}
		
	}
	
}


HexaGrama.prototype.addHexa = function(x, y) {

	// At first, we mus check if there's enough space
	var occupied = this.isOccupied(x, y);
	
	if (!occupied) {
	
		var hexa = new Hexa(x, y, this.ro, this);
		
		hexa.draw();
		
		this.svg.appendChild(hexa.layer);
		
		for (sector in hexa.sectors) {
			if (hexa.checkSector(sector) !== false) {
				this.connections.push(new S.connection(hexa, this.hexas[hexa.checkSector(sector)], sector));
			}
		}
		
		this.hexas.push(hexa);
	} else {
		
		occupied.invadingSpaceAlert();
		
	}
}


/* HexaGrama function for check is the space mented to build an hexa es occupied or not. Is it is, it returns the hexa that occupies the place. */
HexaGrama.prototype.isOccupied = function(xNew, yNew) {
	
	for (var i = 0; i < this.hexas.length; i ++) {
		
		var x = this.hexas[i].x;
		var y = this.hexas[i].y;
		var ro = this.hexas[i].ro;
		
		// Range is that we must allow if is just in the borderline
		var range = 1;
		
		// The arists :)
		var v = new Array();
		
		v['N'] = new S.point(x, y - ro * 2 + range);
		v['NE'] = new S.point(x + ro * 2 * 0.866 - range, y - ro + range);
		v['SE'] = new S.point(x + ro * 2 * 0.866 - range, y + ro - range);
		v['S'] = new S.point(x, y + ro * 2 - range);
		v['SW'] = new S.point(x - ro * 2 * 0.866 + range, y + ro - range);
		v['NW'] = new S.point(x - ro * 2 * 0.866 + range, y - ro + range);
		
		/* The first, and most probable thing is that is not at all near the hexagon. 
		 * We check if the rectangle that is defined by the north, the south, east and west is free */
		if (!(xNew < v['SW'].x || xNew > v['NE'].x || yNew < v['N'].y || yNew > v['S'].y)) { // There's a chance that being inside the rectangle, still is outside the hexagon
		
			// North, South or Ecuator??
			if (yNew > v['N'].y && yNew < v['NW'].y) {
			
				// North West, or North East?
				if (xNew < v['N'].x) {
				
					// Now, North West. Lets do that function :)
					// In the left northern portion the angle makes y to go up in: y = 0.577 x 
					var currentX = xNew - v['SW'].x;
					// We must inverse the Y coordinates in order to solve the ecaution
					var differenceY = v['NW'].y - v['N'].y;
					var currentY = differenceY - (yNew - v['N'].y);
					
					// Now, what y should it be for this x ? 
					var yForX = 0.577 * currentX;
					
					if (yForX > currentY) { // This means is inside. Sad
						return this.hexas[i];
					}
					
				} else {
				
					// Same as north west. But now X is inverted too
					// In the left northern portion the angle makes y to go up in: y = 0.577 x 
					var differenceX = v['NE'].x - v['N'].x;
					var currentX = differenceX - (xNew - v['N'].x);
					// We must inverse the Y coordinates in order to solve the ecaution
					var differenceY = v['NW'].y - v['N'].y;
					var currentY = differenceY - (yNew - v['N'].y);
					
					// Now, what y should it be for this x ? 
					var yForX = 0.577 * currentX;
					
					if (yForX > currentY) { // This means is inside. Sad
						return this.hexas[i];
					}
				}
			} else if (yNew > v['NW'].y && yNew < v['SW'].y) {
				return this.hexas[i];
			} else {
				// South West, or South East?
				if (xNew < v['N'].x) {

					// Same as north west, yet not inverted
					var currentX = xNew - v['SW'].x;
					var currentY = yNew - v['SW'].y;
					
					// Now, what y should it be for this x ? 
					var yForX = 0.577 * currentX;
					
					if (yForX > currentY) { // This means is inside. Sad
						return this.hexas[i];
					}
					
				} else {

					// Same as south west, yet X inverted
					var differenceX = v['SE'].x - v['S'].x;
					var currentX = differenceX - (xNew - v['S'].x);
					var currentY = yNew - v['SW'].y;
					
					// Now, what y should it be for this x ? 
					var yForX = 0.577 * currentX;
					
					if (yForX > currentY) { // This means is inside. Sad
						return this.hexas[i];
					}
				}

			}
/*			
			// Is in left portion?
			if (xNew < v['N'].x && xNew > v['SW'].x) {
				
				// Is in northern portion?
				if (yNew > v['N'].y && yNew < v['NW'].y) {
					
					// In the left northern portion the angle makes y to go up in: y = 1.732 x 
					var currentX = xNew - v['SW'].x;
					// We must inverse the Y coordinates in order to solve the ecaution
					var difference = v['NW'].y - v['N'].y;
					var currentY = difference - (yNew - v['N'].y);
					
					// Now, what y should it be for this x ? 
					var yForX = 0.577 * currentX;
					
					if (yForX > currentY) { // This means is inside. Sad
						return false;
					}
					
				} else if (yNew > v['NW'].y && yNew < v['SW'].y) {
					
					// Right in the middle... inside
					return false;
				
				} else if (yNew > v['SW'].y && yNew < v['S'].y) {
					
					// Southern portion. Allmost same check that northern, without coordinates inversion
					var currentX = xNew - v['SW'].x;
					var currentY = yNew - v['SW'].y;
					
					// Now, what y should it be for this x ? 
					var yForX = 0.577 * currentX;
					
					if (yForX > currentY) { // This means is inside. Sad
						return false; 
					}					
					
				}
				
			} else { // No need to ask, if is not in left, it MUST be in right portion :)
			
				// Is in northern portion?
				if (yNew > v['N'].y && yNew < v['NW'].y) {
										
				} else if (yNew > v['NW'].y && yNew < v['SW'].y) {
					
					// Right in the middle... inside
					return false;
				
				} else if (yNew > v['SW'].y && yNew < v['S'].y) {
					
					
				}				
			
			}
			*/
						
		}
		
	}
	
	return false;
}

HexaGrama.prototype.getSelfIndex = function() {
	for (var i = 0; i < S.hexaGramas.length; i ++) {
		if (S.hexaGramas[i] == this) {
			return i;
		}
	}
}

/* Synonimia */
HexaGrama.prototype.createHexa = function(x, y) {

	this.addHexa(x, y);
	
}

HexaGrama.prototype.newHexa = function(x, y) {

	this.addHexa(x, y);
	
}


///////////////////////////////////////////////////////////////////////////////////////////////
// Hexa core functions

/* Constructor. Creates a new instance of Hexa */
function Hexa(x, y, ro, parent) {
	
	this.x = x;
	this.y = y;
	this.ro = ro;
	this.parent = parent;
	
	this.vertexs = new Array();
	this.vertexs['N'] = new S.point(this.x, this.y - this.ro);
	this.vertexs['NE'] = new S.point(this.x + this.ro * 0.866, this.y - this.ro / 2);
	this.vertexs['SE'] = new S.point(this.x + this.ro * 0.866, this.y + this.ro / 2);
	this.vertexs['S'] = new S.point(this.x, this.y + this.ro);
	this.vertexs['SW'] = new S.point(this.x - this.ro * 0.866, this.y + this.ro / 2);
	this.vertexs['NW'] = new S.point(this.x - this.ro * 0.866, this.y - this.ro / 2);
	
	this.sectors = new Array();
	this.sectors['NE'] = new S.point(this.x + this.ro * 0.866, this.y - this.ro * 1.5);
	this.sectors['E'] = new S.point(this.x + this.ro * 0.866 * 2, this.y);
	this.sectors['SE'] = new S.point(this.x + this.ro * 0.866, this.y + this.ro * 1.5);
	this.sectors['SW'] = new S.point(this.x - this.ro * 0.866, this.y + this.ro * 1.5);
	this.sectors['W'] = new S.point(this.x - this.ro * 0.866 * 2, this.y);
	this.sectors['NW'] = new S.point(this.x - this.ro * 0.866, this.y - this.ro * 1.5);
	
	this.layer = S.svg("g");

	this.path = S.svg("path");
	
	this.layer.appendChild(this.path);
	
	this.layer.onmousedown = function(e) {
		
		S(e.target).addAuxiliaryHexas();
		
	}
}

Hexa.prototype.draw = function() {
	
	/* Stablish the values for the vertex */
	var d = "M " + this.vertexs['N'].x + ", " + this.vertexs['N'].y + " ";
	d += "L " + this.vertexs['NE'].x + ", " + this.vertexs['NE'].y + " ";
	d += "L " + this.vertexs['SE'].x + ", " + this.vertexs['SE'].y + " ";
	d += "L " + this.vertexs['S'].x + ", " + this.vertexs['S'].y + " ";
	d += "L " + this.vertexs['SW'].x + ", " + this.vertexs['SW'].y + " ";
	d += "L " + this.vertexs['NW'].x + ", " + this.vertexs['NW'].y + " ";
	d += "L " + this.vertexs['N'].x + ", " + this.vertexs['N'].y + " ";
	
	this.path.setAttribute("class", "default-hexa");
	this.path.setAttribute("d", d);
	
}

Hexa.prototype.addHexa = function(sector) {
	
	if (this.checkSector(sector) !== false) {
		return false;
	}
	
	switch (sector) {
		case 0:
		case "NE":
		case "North East":
			this.parent.addHexa(this.sectors["NE"].x, this.sectors["NE"].y);
			break;
		case 1:
		case "E":
		case "East":
			this.parent.addHexa(this.sectors["E"].x, this.sectors["E"].y);
			break;
		case 2:
		case "SE":
		case "South East":
			this.parent.addHexa(this.sectors["SE"].x, this.sectors["SE"].y);
			break;
		case 3:
		case "SW":
		case "South West":
			this.parent.addHexa(this.sectors["SW"].x, this.sectors["SW"].y);
			break;
		case 4:
		case "W":
		case "West":
			this.parent.addHexa(this.sectors["W"].x, this.sectors["W"].y);
			break;
		case 5:
		case "NW":
		case "North West":
			this.parent.addHexa(this.sectors["NW"].x, this.sectors["NW"].y);
			break;
	}
}

Hexa.prototype.addAuxiliaryHexas = function() {

	this.auxiliaryHexas = new Array();
	
	var sectors = this.getFreeSectors();
	
	for (var i = 0; i < sectors.length; i ++) {
		var auxHexa = new AuxHexa(this.sectors[sectors[i]].x, this.sectors[sectors[i]].y, this.ro, this.parent);
		this.auxiliaryHexas.push(auxHexa);
	}
	
}

Hexa.prototype.checkSector = function(sector) {
	
	// For God sake, rename this.parent.hexas
	var hexas = this.parent.hexas;
	
	switch (sector) {
		case 0:
		case "NE":
		case "North East":
			for (var i = 0; i < hexas.length; i ++) {
				if (hexas[i].x >= this.sectors["NE"].x - 2 && hexas[i].x <= this.sectors["NE"].x + 2 && hexas[i].y >= this.sectors["NE"].y - 2 && hexas[i].y <= this.sectors["NE"].y + 2) {
					return hexas[i];
				}
			}
			return false;
			break;
		case 1:
		case "E":
		case "East":
			for (var i = 0; i < hexas.length; i ++) {
				if (hexas[i].x >= this.sectors["E"].x - 2 && hexas[i].x <= this.sectors["E"].x + 2 && hexas[i].y >= this.sectors["E"].y - 2 && hexas[i].y <= this.sectors["E"].y + 2) {
					return hexas[i];
				}
			}
			return false;
			break;
		case 2:
		case "SE":
		case "South East":
			for (var i = 0; i < hexas.length; i ++) {
				if (hexas[i].x >= this.sectors["SE"].x - 2 && hexas[i].x <= this.sectors["SE"].x + 2 && hexas[i].y >= this.sectors["SE"].y - 2 && hexas[i].y <= this.sectors["SE"].y + 2) {
					return hexas[i];
				}
			}
			return false;
			break;
		case 3:
		case "SW":
		case "South West":
			for (var i = 0; i < hexas.length; i ++) {
				if (hexas[i].x >= this.sectors["SW"].x - 2 && hexas[i].x <= this.sectors["SW"].x + 2 && hexas[i].y >= this.sectors["SW"].y - 2 && hexas[i].y <= this.sectors["SW"].y + 2) {
					return hexas[i];
				}
			}
			return false;
			break;
		case 4:
		case "W":
		case "West":
			for (var i = 0; i < hexas.length; i ++) {
				if (hexas[i].x >= this.sectors["W"].x - 2 && hexas[i].x <= this.sectors["W"].x + 2 && hexas[i].y >= this.sectors["W"].y - 2 && hexas[i].y <= this.sectors["W"].y + 2) {
					return hexas[i];
				}
			}
			return false;
			break;
		case 5:
		case "NW":
		case "North West":
			for (var i = 0; i < hexas.length; i ++) {
				if (hexas[i].x >= this.sectors["NW"].x - 2 && hexas[i].x <= this.sectors["NW"].x + 2 && hexas[i].y >= this.sectors["NW"].y - 2 && hexas[i].y <= this.sectors["NW"].y + 2) {
					return hexas[i];
				}
			}
			return false;
			break;
	}
	
}

Hexa.prototype.getFreeSectors = function() {
	
	var freeSectors = new Array();
	
	if (this.checkSector("NE") == false) {
		freeSectors.push("NE");
	}
	if (this.checkSector("E") == false) {
		freeSectors.push("E");
	}
	if (this.checkSector("SE") == false) {
		freeSectors.push("SE");
	}
	if (this.checkSector("SW") == false) {
		freeSectors.push("SW");
	}
	if (this.checkSector("W") == false) {
		freeSectors.push("W");
	}
	if (this.checkSector("NW") == false) {
		freeSectors.push("NW");
	}
	
	return freeSectors;
	
}

Hexa.prototype.invadingSpaceAlert = function() {
	
	this.auxHexaInvadingSpaceAlerter = null;
	
	this.auxHexaInvadingSpaceAlerter = new AuxHexa(this.x, this.y, this.ro * 2, this.parent);
	
	this.auxHexaInvadingSpaceAlerter.blinkAndDestroy(6, 100);
	
}

function AuxHexa(x, y, ro, parent) {
	
	this.x = x;
	this.y = y;
	this.ro = ro;
	this.parent = parent;
	
	this.vertexs = new Array();
	this.vertexs['N'] = new S.point(this.x, this.y - this.ro);
	this.vertexs['NE'] = new S.point(this.x + this.ro * 0.866, this.y - this.ro / 2);
	this.vertexs['SE'] = new S.point(this.x + this.ro * 0.866, this.y + this.ro / 2);
	this.vertexs['S'] = new S.point(this.x, this.y + this.ro);
	this.vertexs['SW'] = new S.point(this.x - this.ro * 0.866, this.y + this.ro / 2);
	this.vertexs['NW'] = new S.point(this.x - this.ro * 0.866, this.y - this.ro / 2);
		
	this.layer = this.parent.auxLayer;

	this.path = S.svg("path");
	
	this.layer.appendChild(this.path);
	
	/* Stablish the values for the vertex */
	var d = "M " + this.vertexs['N'].x + ", " + this.vertexs['N'].y + " ";
	d += "L " + this.vertexs['NE'].x + ", " + this.vertexs['NE'].y + " ";
	d += "L " + this.vertexs['SE'].x + ", " + this.vertexs['SE'].y + " ";
	d += "L " + this.vertexs['S'].x + ", " + this.vertexs['S'].y + " ";
	d += "L " + this.vertexs['SW'].x + ", " + this.vertexs['SW'].y + " ";
	d += "L " + this.vertexs['NW'].x + ", " + this.vertexs['NW'].y + " ";
	d += "L " + this.vertexs['N'].x + ", " + this.vertexs['N'].y + " ";
	
	this.path.setAttribute("class", "auxiliary-hexa");
	this.path.setAttribute("d", d);
	
	this.parent.auxHexas.push(this);
}

AuxHexa.prototype.getSelfIndex = function() {
	for (var i = 0; i < this.parent.auxHexas.length; i ++) {
		if (this.parent.auxHexas[i] == this) {
			return i;
		}
	}
}

AuxHexa.prototype.blinkAndDestroy = function(times, speed) {
	
	if (times > 0) {
		times --;
		if (this.path.getAttribute("class") == "auxiliary-hexa") {
			this.path.setAttribute("class", "invisible-hexa");
			setTimeout("S.hexaGramas[" + this.parent.getSelfIndex() + "].auxHexas[" + this.getSelfIndex() + "].blinkAndDestroy(" + times + ", " + speed + ")", speed * 2 / 3);
		} else {
			this.path.setAttribute("class", "auxiliary-hexa");
			setTimeout("S.hexaGramas[" + this.parent.getSelfIndex() + "].auxHexas[" + this.getSelfIndex() + "].blinkAndDestroy(" + times + ", " + speed + ")", speed * 3 / 2);
		}
	
	} else {
		
		// Sometimes, dunno why, it throws an error of "Node not found", code 8. I suppose it means it was deleted before, of some of the kind, so, well, let catch it
		// This is a bad hack, but seems harmfulless
		try {
			this.parent.auxLayer.removeChild(this.path);
		} catch (err) {}
		
	}
}

////////////////////////////////////////////////////////////////////
// This testing script

var hexaGrama;

window.onload = function() {
	
	hexaGrama = new HexaGrama(1100, 600, 40);
	S.el("map").appendChild(hexaGrama.svg);

}
</script>

	</head>
	
	<body>
		<div id="map">
		</div>
	</body>
	
</html>